Vue 模板编译原理 · Issue #18 · berwin/Blog · GitHub 您所在的位置:网站首页 vue 模板继承 Vue 模板编译原理 · Issue #18 · berwin/Blog · GitHub

Vue 模板编译原理 · Issue #18 · berwin/Blog · GitHub

#Vue 模板编译原理 · Issue #18 · berwin/Blog · GitHub| 来源: 网络整理| 查看: 265

Vue 模板编译原理

关于vue的内部原理其实有很多个重要的部分,变化侦测,模板编译,virtualDOM,整体运行流程等。

之前写过一篇《深入浅出 - vue变化侦测原理》 讲了关于变化侦测的实现原理。

那今天主要把 模板编译 这部分的实现原理单独拿出来讲一讲。

本文我可能不会在文章中说太多细节部分的处理,我会把 vue 对模板编译这部分的整体原理讲清楚,主要是让读者读完文章后对模板编译的整体实现原理有一个清晰的思路和理解。

关于 Vue 编译原理这块的整体逻辑主要分三个部分,也可以说是分三步,这三个部分是有前后关系的:

第一步是将 模板字符串 转换成 element ASTs(解析器) 第二步是对 AST 进行静态节点标记,主要用来做虚拟DOM的渲染优化(优化器) 第三步是 使用 element ASTs 生成 render 函数代码字符串(代码生成器) 解析器

解析器主要干的事是将 模板字符串 转换成 element ASTs,例如:

{{name}}

上面这样一个简单的 模板 转换成 element AST 后是这样的:

{ tag: "div" type: 1, staticRoot: false, static: false, plain: true, parent: undefined, attrsList: [], attrsMap: {}, children: [ { tag: "p" type: 1, staticRoot: false, static: false, plain: true, parent: {tag: "div", ...}, attrsList: [], attrsMap: {}, children: [{ type: 2, text: "{{name}}", static: false, expression: "_s(name)" }] } ] }

我们先用这个简单的例子来说明这个解析器的内部究竟发生了什么。

这段模板字符串会扔到 while 中去循环,然后 一段一段 的截取,把截取到的 每一小段字符串 进行解析,直到最后截没了,也就解析完了。

上面这个简单的模板截取的过程是这样的:

{{name}} {{name}} {{name}} {{name}}

那是根据什么截的呢?换句话说截取字符串有什么规则么?

当然有

只要判断模板字符串是不是以 const ncname = '[a-zA-Z_][\\w\\-\\.]*' const qnameCapture = `((?:${ncname}\\:)?${ncname})` const startTagOpen = new RegExp(`^/ let html = `` let index = 0 const start = html.match(startTagOpen) const match = { tagName: start[1], attrs: [], start: 0 } html = html.substring(start[0].length) index += start[0].length let end, attr while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) { html = html.substring(attr[0].length) index += attr[0].length match.attrs.push(attr) } if (end) { match.unarySlash = end[1] html = html.substring(end[0].length) index += end[0].length match.end = index } console.log(match) Stack

用正则把 开始标签 中包含的数据(attrs, tagName 等)解析出来之后还要做一个很重要的事,就是要维护一个 stack。

那这个 stack 是用来干什么的呢?

这个 stack 是用来记录一个层级关系的,用来记录DOM的深度。

更准确的说,当解析到一个 开始标签 或者 文本,无论是什么, stack 中的最后一项,永远是当前正在被解析的节点的 parentNode 父节点。

通过 stack 解析器就可以把当前解析到的节点 push 到 父节点的 children 中。

也可以把当前正在解析的节点的 parent 属性设置为 父节点。

事实上也确实是这么做的。

但并不是只要解析到一个标签的开始部分就把当前标签 push 到 stack 中。

因为在 HTML 中有一种 自闭合标签,比如 input。

这种 自闭合的标签 是不需要 push 到 stack 中的,因为 input 并不存在子节点。

所以当解析到一个标签的开始时,要判断当前被解析的标签是否是自闭合标签,如果不是自闭合标签才 push 到 stack 中。

if (!unary) { currentParent = element stack.push(element) }

现在有了 DOM 的层级关系,也可以解析出DOM的 开始标签,这样每解析一个 开始标签 就生成一个 ASTElement (存储当前标签的attrs,tagName 等信息的object)

并且把当前的 ASTElement push 到 parentNode 的 children 中,同时给当前 ASTElement 的 parent 属性设置为 stack 中的最后一项

currentParent.children.push(element) element.parent = currentParent


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有